package alipay

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"

	"jgt-wallet/src/gopay/util"

	"github.com/buger/jsonparser"
)

type AppConfig struct {
	SellerID   string `xml:"seller_id"` //合作者ID
	AppID      string `xml:"app_id"`    // 应用ID
	PrivateKey string `xml:"app_private_key"`
	PublicKey  string `xml:"app_public_key"`
	AliKey     string `xml:"ali_public_key"`
}

type AppClient struct {
	Name       string //名称
	SellerID   string //合作者ID
	AppID      string // 应用ID
	PrivateKey *rsa.PrivateKey
	PublicKey  *rsa.PublicKey
	AliKey     *rsa.PublicKey
}

// NewAppClient 创建新支付宝APP支付
func NewAppClient(key string, conf *AppConfig) (*AppClient, error) {
	var (
		client = new(AppClient)
		block  *pem.Block
		pub    interface{}
		err    error
	)
	if conf.SellerID == "" ||
		conf.AppID == "" ||
		conf.PrivateKey == "" ||
		conf.PublicKey == "" ||
		conf.AliKey == "" {
		return nil, errors.New("缺少必要参数")

	}
	client.Name = key
	client.SellerID = conf.SellerID
	client.AppID = conf.AppID

	//APP PrivateKey
	block, _ = pem.Decode([]byte(conf.PrivateKey))

	client.PrivateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {

		return nil, err
	}

	//APP PublicKey
	block, _ = pem.Decode([]byte(conf.PublicKey))

	pub, err = x509.ParsePKIXPublicKey(block.Bytes)
	client.PublicKey = pub.(*rsa.PublicKey)
	if err != nil {
		fmt.Println(err.Error())
		return nil, err
	}

	//ALI PublicKey
	block, _ = pem.Decode([]byte(conf.AliKey))

	pub, err = x509.ParsePKIXPublicKey(block.Bytes)
	client.AliKey = pub.(*rsa.PublicKey)
	if err != nil {
		return nil, err
	}
	return client, err
}

func (a *AppClient) Pay(charge *Charge) (map[string]string, error) {
	var m = make(map[string]string)
	var bizContent = make(map[string]string)
	m["app_id"] = a.AppID
	m["method"] = "alipay.trade.app.pay"
	//	m["format"] = "JSON"
	m["charset"] = "utf-8"
	m["timestamp"] = time.Now().Format("2006-01-02 15:04:05")
	m["version"] = "1.0"
	m["notify_url"] = charge.CallbackURL
	m["sign_type"] = "RSA2"
	bizContent["subject"] = util.TruncatedText(charge.Describe, 32)
	bizContent["out_trade_no"] = charge.TradeNum
	bizContent["product_code"] = "QUICK_MSECURITY_PAY"
	bizContent["total_amount"] = util.MoneyFeeToYuanString(charge.MoneyFee)
	bizContent["timeout_express"] = "30m"
	bizContent["body"] = charge.Describe

	bizContentJson, err := json.Marshal(bizContent)
	if err != nil {
		return map[string]string{}, errors.New("json.Marshal: " + err.Error())
	}
	m["biz_content"] = string(bizContentJson)

	m["sign"] = a.GenSign(m)

	return map[string]string{"orderString": a.ToURL(m)}, nil
}

func (a *AppClient) PayToClient(charge *Charge) (map[string]string, error) {
	return map[string]string{}, errors.New("暂未开发该功能")
}

// QueryOrder 订单查询
func (a *AppClient) QueryOrder(outTradeNo string) (*AliWebAppQueryResult, error) {
	var (
		m   = make(map[string]string)
		url string
	)

	m["method"] = "alipay.trade.query"
	m["app_id"] = a.AppID
	m["format"] = "JSON"
	m["charset"] = "utf-8"
	m["timestamp"] = time.Now().Format("2006-01-02 15:04:05")
	m["version"] = "1.0"
	m["sign_type"] = "RSA2"
	bizContent := map[string]string{"out_trade_no": outTradeNo}
	bizContentJson, err := json.Marshal(bizContent)
	if err != nil {
		return nil, errors.New("json.Marshal: " + err.Error())
	}
	m["biz_content"] = string(bizContentJson)
	sign := a.GenSign(m)
	m["sign"] = sign

	if a.Name == "test" {
		url = fmt.Sprintf("%s?%s", "https://openapi.alipaydev.com/gateway.do", a.ToURL(m))
	} else {
		url = fmt.Sprintf("%s?%s", "https://openapi.alipay.com/gateway.do", a.ToURL(m))
	}

	return GetAlipayApp(url)
}

// GenSign 产生签名
func (a *AppClient) GenSign(m map[string]string) string {
	var data []string

	for k, v := range m {
		if v != "" && k != "sign" {
			data = append(data, fmt.Sprintf(`%s=%s`, k, v))
		}
	}
	sort.Strings(data)
	signData := strings.Join(data, "&")

	s := sha256.New()
	_, err := s.Write([]byte(signData))
	if err != nil {
		panic(err)
	}
	hashByte := s.Sum(nil)
	signByte, err := a.PrivateKey.Sign(rand.Reader, hashByte, crypto.SHA256)
	if err != nil {
		panic(err)
	}

	return base64.StdEncoding.EncodeToString(signByte)
}

// CheckSign 检测签名
func (a *AppClient) CheckSign(signData, sign string) error {
	signByte, err := base64.StdEncoding.DecodeString(sign)
	if err != nil {
		return err
	}
	s := crypto.SHA256.New()
	_, err = s.Write([]byte(signData))
	if err != nil {
		return err
	}
	hash := s.Sum(nil)
	err = rsa.VerifyPKCS1v15(a.AliKey, crypto.SHA256, hash, signByte)
	return err
}

// ToURL
func (a *AppClient) ToURL(m map[string]string) string {
	var buf []string
	for k, v := range m {
		buf = append(buf, fmt.Sprintf("%s=%s", k, url.QueryEscape(v)))
	}
	return strings.Join(buf, "&")
}

// CheckResult 检查支付结果
func (a *AppClient) CheckResult(result []byte) (*AliAppPayResp, error) {

	resp := new(AliAppPayResp)
	if json.Valid(result) {
		signData, _ := jsonparser.GetUnsafeString(result, "alipay_trade_app_pay_response")
		sign, _ := jsonparser.GetString(result, "sign")
		ty, _ := jsonparser.GetString(result, "sign_type")
		if ty != "RSA2" {
			return nil, fmt.Errorf("签名格式异常")
		}
		err := a.CheckSign(signData, sign)
		if err != nil {
			return nil, fmt.Errorf("延签失败")
		}
		err = json.Unmarshal([]byte(signData), resp)
		if err != nil {
			return nil, err
		}
	} else {
		return nil, fmt.Errorf("参数异常")
	}

	return resp, nil
}

// CallBack
func (a *AppClient) CallBack(w http.ResponseWriter, r *http.Request) (*AliWebPayResult, error) {
	var result string
	defer func() {
		w.Write([]byte(result))
	}()

	var m = make(map[string]string)
	var signSlice []string
	r.ParseForm()
	for k, v := range r.Form {
		m[k] = v[0]
		if k == "sign" || k == "sign_type" {
			continue
		}
		signSlice = append(signSlice, fmt.Sprintf("%s=%s", k, v[0]))
	}
	sort.Strings(signSlice)
	signData := strings.Join(signSlice, "&")
	if m["sign_type"] != "RSA2" {
		result = "error"
		panic("签名类型未知")
	}

	a.CheckSign(signData, m["sign"])

	mByte, err := json.Marshal(m)
	if err != nil {
		result = "error"
		return nil, err
	}

	var aliPay AliWebPayResult
	err = json.Unmarshal(mByte, &aliPay)
	if err != nil {
		result = "error"
		panic(fmt.Sprintf("m is %v, err is %v", m, err))
	}
	result = "success"
	return &aliPay, nil
}

//对支付宝者查订单
func GetAlipayApp(urls string) (*AliWebAppQueryResult, error) {
	var aliPay = new(AliWebAppQueryResult)

	re, err := util.HTTPSC.GetData(urls)
	if err != nil {
		return aliPay, errors.New("HTTPSC.PostData: " + err.Error())
	}

	err = json.Unmarshal(re, aliPay)

	return aliPay, err
}
